package database

import (
	"sync"

	"gitee.com/xxhsjp/x-core/conf"
	"gitee.com/xxhsjp/x-core/model"
	"gitee.com/xxhsjp/x-core/utils/xstrings"
	"github.com/pkg/errors"
	"github.com/redis/go-redis/v9"
	"github.com/sirupsen/logrus"
)

var mu sync.RWMutex
var redisDbs = make(map[string]*redis.Client)

func Redis(dsName ...string) *redis.Client {
	var ds string
	if len(dsName) > 0 {
		ds = dsName[0]
	} else {
		ds = "redis"
	}

	return newRedis(ds)
}

func newRedis(ds string) *redis.Client {
	mu.RLock()
	if _, ok := redisDbs[ds]; ok {
		mu.RUnlock()
		return redisDbs[ds]
	}
	mu.RUnlock()

	mu.Lock()
	defer mu.Unlock()
	if _, ok := redisDbs[ds]; !ok {
		redisDbs[ds] = initRedis(redisConfig(ds))
	}

	return redisDbs[ds]
}

func initRedis(c *model.RedisConfig) (rc *redis.Client) {

	if len(c.SentinelAddrs) > 0 {
		rc = redis.NewFailoverClient(&redis.FailoverOptions{
			MasterName:       c.MasterName,
			SentinelAddrs:    c.SentinelAddrs,
			SentinelUsername: c.SentinelUsername,
			SentinelPassword: c.SentinelPassword,
		})
	} else {
		rc = redis.NewClient(&redis.Options{
			Addr:     c.Addr,
			Password: c.Password, // no password set ""
			DB:       c.DB,       // use default DB 0
			Username: c.Username,
		})
	}

	return
}

func redisConfig(ds string) *model.RedisConfig {
	c := model.RedisConfig{}
	viper := conf.Wrapper

	err := viper.UnmarshalKey(ds, &c)
	if err != nil {
		logrus.Errorf("%+v", errors.WithStack(err))
		panic(err)
	}
	if xstrings.IsBlank(c.Addr) && len(c.SentinelAddrs) == 0 {
		err := "redis config is invalid"
		logrus.Errorf("%+v", errors.New(err))
		panic(err)
	}

	pwd, err := viper.GetDecryptedString(c.Password, true)
	if err != nil {
		logrus.Errorf("%+v", errors.WithStack(err))
		panic(err)
	}
	c.Password = pwd
	logrus.Debugf("redis config[%s]: %+v", ds, c)
	return &c
}
